package com.aplink.generic.util.validation;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.regex.Pattern;

import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.EditText;

import com.aplink.generic.GenericFragment;
import com.aplink.generic.constant.type.ControlType;
import com.aplink.generic.constant.type.EditTextType;
import com.aplink.generic.database.DatabaseHelper;
import com.aplink.generic.database.SqlMethods;
import com.aplink.generic.pojo.GenericObject;
import com.aplink.generic.pojo.ObjPropInfo;
import com.aplink.generic.pojo.controlspec.ButtonSpec;
import com.aplink.generic.pojo.controlspec.ControlSpec;
import com.aplink.generic.pojo.controlspec.EditTextSpec;
import com.aplink.generic.util.GpsUtils;
import com.aplink.generic.util.LogUtil;
import com.aplink.generic.widgets.ApLinkBaseView;
import com.aplink.generic.widgets.ApLinkEditText;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;

public class ControlValidationTask extends
        AsyncTask<ControlSpec, Void, ValidationResult> implements
        LocationListener, GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener {
    private boolean isUpdateDistance = false;
    private ControlSpec mControlSpec = null;
    private final WeakReference<GenericFragment> mGenericFragment;
    private Location mGPSLocation = null;
    private LocationClient mLocationClient;
    private LocationRequest mLocationRequest;

    public ControlValidationTask(final GenericFragment genericFragment) {
        mGenericFragment = new WeakReference<GenericFragment>(genericFragment);
    }

    @Override
    protected ValidationResult doInBackground(final ControlSpec... params) {
        mControlSpec = params[0];
        final ValidationResult mValidationResult = new ValidationResult();
        final String validationGroupName = mControlSpec.getValidationGroup();
        if (TextUtils.isEmpty(validationGroupName)) {
            mValidationResult.setValid(true);
            return mValidationResult;
        }
        boolean isValid = true;
        final ArrayList<ControlSpec> validationControls = mGenericFragment
                .get().getValidationGroups().get(validationGroupName);
        for (final ControlSpec controlSpec : validationControls) {
            final String validationGroup = controlSpec.getValidationGroup();
            if (validationGroup == null) {
                continue;
            }
            if (!controlSpec.getValidationGroup().equalsIgnoreCase(
                    validationGroup)) {
                continue;
            }
            final ApLinkBaseView baseView = mGenericFragment.get()
                    .getAPLinkViewByID(controlSpec.getIDName());
            if (controlSpec.getControlType().equals(ControlType.EDITTEXT)) {
                String value = "";
                if ((baseView.getAttribute() != null)
                        && (baseView.getAttribute().getValue() != null)) {
                    value = baseView.getAttribute().getValue().toString();
                }
                final ApLinkEditText customEdittext = (ApLinkEditText) baseView;
                final EditTextSpec editTextSpecFormat = (EditTextSpec) controlSpec;
                final Validation validation = editTextSpecFormat
                        .getValidation();
                final EditText editText = customEdittext.getEditText();
                final boolean isNoBlank = validation.isNoBlank();
                final boolean isNoDuplicate = validation.isNoDuplicate();
                final int minLength = editTextSpecFormat.getMinLength();
                final int maxLength = editTextSpecFormat.getMaxLength();
                String errorMessage = null;
                if (isNoDuplicate) {
                    final String whereClause = SqlMethods.getInstance()
                            .equal(controlSpec.getMappingName(), value)
                            .toString();
                    final boolean isExists = DatabaseHelper.getInstance()
                            .isExists(mGenericFragment.get().getContentData(),
                                    whereClause);
                    if (isExists) {
                        setErrorText(editText, validation.getDuplicate()
                                .getMessage());
                        isValid = false;
                        continue;
                    }
                }
                if (isNoBlank && value.isEmpty()) {
                    if (TextUtils.isEmpty(validation.getBlankRule()
                            .getMessage())) {
                        errorMessage = "This field is required";
                    } else {
                        errorMessage = validation.getBlankRule().getMessage();
                    }
                    setErrorText(editText, errorMessage);
                    isValid = false;
                    continue;
                }
                if (value.length() < minLength) {
                    setErrorText(editText, " longer than " + minLength
                            + " characters");
                    isValid = false;
                    continue;
                }
                if (value.length() > maxLength) {
                    setErrorText(editText, " no longer than " + maxLength
                            + " characters");
                    isValid = false;
                    continue;
                }

                if (editTextSpecFormat.getDataInputType().equals(
                        EditTextType.EMAIL)) {
                    final Pattern email = android.util.Patterns.EMAIL_ADDRESS;
                    if (!email.matcher(value).matches()) {
                        setErrorText(editText,
                                "This field is invalid, Please enter your email");
                        isValid = false;
                        continue;
                    }
                }

                if (editTextSpecFormat.getDataInputType().equals(
                        EditTextType.PHONE)) {
                    final Pattern email = android.util.Patterns.PHONE;
                    if (!email.matcher(value).matches()) {
                        setErrorText(editText,
                                "This field is invalid, Please enter phone numbers");
                        isValid = false;
                        continue;
                    }
                }

                if (editTextSpecFormat.getDataInputType().equals(
                        EditTextType.PASSWORD)) {
                    final String passwordRegex = editTextSpecFormat
                            .getRegularExpression();
                    if (passwordRegex == null) {
                        continue;
                    }
                    final Pattern password = Pattern.compile(passwordRegex);
                    if (!password.matcher(value).matches()) {
                        errorMessage = "Passwords must be at least 4 characters and contain at least two of the following: alphabets and numbers.";
                        setErrorText(editText, errorMessage);
                        isValid = false;
                        continue;
                    }
                }
            } else if (controlSpec.getControlType().equals(ControlType.BUTTON)) {
                final ButtonSpec buttonSpec = (ButtonSpec) controlSpec;
                final Validation validation = buttonSpec.getValidation();
                if (validation.isValidLocation()) {
                    final LocationManager manager = (LocationManager) mGenericFragment
                            .get().getApplicationContext()
                            .getSystemService(Context.LOCATION_SERVICE);
                    if (!manager
                            .isProviderEnabled(LocationManager.GPS_PROVIDER)) {
                        GpsUtils.turnGPSOn();
                    }
                    mLocationRequest = LocationRequest.create();
                    mLocationRequest.setInterval(5);
                    mLocationRequest
                            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
                    mLocationRequest.setFastestInterval(1);
                    mLocationClient = new LocationClient(mGenericFragment.get()
                            .getActivity(), this, this);
                    mLocationClient.connect();
                    final ObjPropInfo mLatitude = mGenericFragment.get()
                            .getContentData().getLatitudeCol();
                    final ObjPropInfo mLongitude = mGenericFragment.get()
                            .getContentData().getLongitudeCol();
                    final GenericObject genericObject = mGenericFragment.get()
                            .getBindingObject();
                    final double latitude = Double.parseDouble(genericObject
                            .getValue(mLatitude));
                    final double longitude = Double.parseDouble(genericObject
                            .getValue(mLongitude));
                    final Location compareLocation = new Location(
                            "compareLocation");
                    compareLocation.setLatitude(latitude);
                    compareLocation.setLongitude(longitude);
                    boolean isChecking = true;
                    while (isChecking) {
                        if (mLocationClient.isConnected()
                                && (mGPSLocation != null)) {
                            if (isUpdateDistance) {
                                isUpdateDistance = false;
                                if (mGPSLocation.distanceTo(compareLocation) <= validation
                                        .getLocationValidation().getDistance()) {
                                    isValid = true;
                                    isChecking = false;
                                    stopPeriodicUpdates();
                                    mLocationClient.disconnect();
                                } else {
                                    isValid = false;
                                    mValidationResult.setMessage(validation
                                            .getLocationValidation()
                                            .getMessage());
                                }
                                if (mGPSLocation.getAccuracy() <= validation
                                        .getLocationValidation().getAccuracy()) {
                                    stopPeriodicUpdates();
                                    mLocationClient.disconnect();
                                    isChecking = false;
                                }
                            }
                        }
                    }
                    GpsUtils.turnGPSOff();
                }
            }
        }
        mValidationResult.setValid(isValid);
        return mValidationResult;
    }

    @Override
    public void onConnected(final Bundle arg0) {
        startPeriodicUpdates();
    }

    @Override
    public void onConnectionFailed(final ConnectionResult arg0) {

    }

    @Override
    public void onDisconnected() {

    }

    @Override
    public void onLocationChanged(final Location location) {
        mGPSLocation = location;
        isUpdateDistance = true;
        LogUtil.info(
                "GPS Location:(" + location.getLatitude() + ","
                        + location.getLongitude() + ")", "onLocationChanged",
                getClass());
    }

    @Override
    protected void onPostExecute(final ValidationResult result) {
        mGenericFragment.get().handleValidationResult(mControlSpec, result);
        super.onPostExecute(result);
    }

    private void setErrorText(final EditText editText, final String params) {
        mGenericFragment.get().getActivity().runOnUiThread(new Runnable() {

            @Override
            public void run() {
                editText.setError(params);
            }
        });
    }

    private void startPeriodicUpdates() {
        mLocationClient.requestLocationUpdates(mLocationRequest, this);
    }

    private void stopPeriodicUpdates() {
        mLocationClient.removeLocationUpdates(this);
    }
}
